隆Hola 馃憢! Espera mientras comienza la sesi贸n.

Antes que todo, 驴c贸mo est谩n?

Visualizaci贸n de Informaci贸n

IIC2026 2020-2

Utilidades en D3.js II

Visualizaci贸n de Informaci贸n

IIC2026 2020-2

Repaso

Repaso


1. Eventos en D3.js

2. Transiciones de D3.js

3. Join de datos personalizado

隆M谩s detalles para nuestro programa!


Comenzamos con una visualizaci贸n est谩tica.


Terminamos con una visualuzaci贸n din谩mica e interactiva.

Eventos



const width = 600;
const height = 400;
const margin = {
  top: 30,
  bottom: 30,
  right: 30,
  left: 30,
};

const svg = d3
  .select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

const boton = d3.select("body").append("button").text("Agregar elemento");

const parrafo = d3.select("body").append("p");

const contenedorEjeY = svg
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);

const contenedorEjeX = svg
  .append("g")
  .attr("transform", `translate(${margin.left}, ${height - margin.bottom})`);

const contenedorBarras = svg
  .append("g")
  .attr("transform", `translate(${margin.left} ${margin.top})`);

function joinDeDatos(datos) {
  const maximaFrecuencia = d3.max(datos, (d) => d.frecuencia);

  const escalaAltura = d3
    .scaleLinear()
    .domain([0, maximaFrecuencia])
    .range([0, height - margin.top - margin.bottom]);

  const escalaY = d3
    .scaleLinear()
    .domain([0, maximaFrecuencia])
    .range([height - margin.top - margin.bottom, 0]);

  const ejeY = d3.axisLeft(escalaY);

  contenedorEjeY
    .transition()
    .duration(1000)
    .call(ejeY)
    .selection()
    .selectAll("line")
    .attr("x1", width - margin.right - margin.left)
    .attr("stroke-dasharray", "5")
    .attr("opacity", 0.5);

  const escalaX = d3
    .scaleBand()
    .domain(datos.map((d) => d.categoria))
    .rangeRound([0, width - margin.right - margin.left])
    .padding(0.5);

  const ejeX = d3.axisBottom(escalaX);

  contenedorEjeX
    .transition()
    .duration(1000)
    .call(ejeX)
    .selection()
    .selectAll("text")
    .attr("font-size", 20);

  contenedorBarras
    .selectAll("rect")
    .data(datos, (d) => d.categoria)
    .join(
      (enter) =>
        enter
          .append("rect")
          .attr("fill", "magenta")
          .attr("y", height - margin.top - margin.bottom)
          .attr("x", (d) => escalaX(d.categoria))
          .attr("width", escalaX.bandwidth())
          .attr("height", 0)
          .transition()
          .duration(1000)
          .attr("height", (d) => escalaAltura(d.frecuencia))
          .attr("y", (d) => escalaY(d.frecuencia))
          .selection(),
      (update) =>
        update
          .transition()
          .duration(1000)
          .attr("height", (d) => escalaAltura(d.frecuencia))
          .attr("y", (d) => escalaY(d.frecuencia))
          .attr("x", (d) => escalaX(d.categoria))
          .attr("width", escalaX.bandwidth())
          .selection(),
      (exit) =>
        exit
          .transition()
          .duration(500)
          .attr("y", height - margin.top - margin.bottom)
          .attr("height", 0)
          .remove()
    )
    .on("mouseenter", (_, d) => {
      parrafo.text(`Categor铆a: ${d.categoria}, Frecuencia: ${d.frecuencia}`);
    })
    .on("mouseleave", () => {
      parrafo.text("");
    })
    .on("click", (_, d) => {
      datos.splice(datos.indexOf(d), 1);
      joinDeDatos(datos);
    });
}

const datoNuevoRandom = (datos) => ({
  categoria: String.fromCharCode(
    datos[datos.length - 1].categoria.charCodeAt(0) + 1
  ),
  frecuencia: Math.floor(Math.random() * 800),
});

let datos;

d3.json("datos.json")
  .then((datosCargados) => {
    console.log(datosCargados);
    datos = datosCargados;
    joinDeDatos(datos);
    boton.on("click", () => {
      datos.push(datoNuevoRandom(datos));
      joinDeDatos(datos);
    });
  })
  .catch((error) => console.log(error));
          

Eventos


隆Hay muchos tipos de eventos!


  • click
  • dbclick
  • change
  • dragstart
  • dragover
  • ...

Lista de eventos: MDN

隆Alerta de versi贸n!


V5:


            seleccion.on("click", (d, i, elements) => {
              console.log(d3.event());
              console.log(d);
              console.log(elements[i]);
            })
          

V6:


            seleccion.on("click", (event, d) => {
              console.log(event);
              console.log(d);
              console.log(event.currentTarget);
            })
          

Transiciones



const width = 600;
const height = 400;
const margin = {
  top: 30,
  bottom: 30,
  right: 30,
  left: 30,
};

const svg = d3
  .select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

const boton = d3.select("body").append("button").text("Agregar elemento");

const parrafo = d3.select("body").append("p");

const contenedorEjeY = svg
  .append("g")
  .attr("transform", `translate(${margin.left}, ${margin.top})`);

const contenedorEjeX = svg
  .append("g")
  .attr("transform", `translate(${margin.left}, ${height - margin.bottom})`);

const contenedorBarras = svg
  .append("g")
  .attr("transform", `translate(${margin.left} ${margin.top})`);

function joinDeDatos(datos) {
  const maximaFrecuencia = d3.max(datos, (d) => d.frecuencia);

  const escalaAltura = d3
    .scaleLinear()
    .domain([0, maximaFrecuencia])
    .range([0, height - margin.top - margin.bottom]);

  const escalaY = d3
    .scaleLinear()
    .domain([0, maximaFrecuencia])
    .range([height - margin.top - margin.bottom, 0]);

  const ejeY = d3.axisLeft(escalaY);

  contenedorEjeY
    .transition()
    .duration(1000)
    .call(ejeY)
    .selection()
    .selectAll("line")
    .attr("x1", width - margin.right - margin.left)
    .attr("stroke-dasharray", "5")
    .attr("opacity", 0.5);

  const escalaX = d3
    .scaleBand()
    .domain(datos.map((d) => d.categoria))
    .rangeRound([0, width - margin.right - margin.left])
    .padding(0.5);

  const ejeX = d3.axisBottom(escalaX);

  contenedorEjeX
    .transition()
    .duration(1000)
    .call(ejeX)
    .selection()
    .selectAll("text")
    .attr("font-size", 20);

  contenedorBarras
    .selectAll("rect")
    .data(datos, (d) => d.categoria)
    .join(
      (enter) =>
        enter
          .append("rect")
          .attr("fill", "magenta")
          .attr("y", height - margin.top - margin.bottom)
          .attr("x", (d) => escalaX(d.categoria))
          .attr("width", escalaX.bandwidth())
          .attr("height", 0)
          .transition()
          .duration(1000)
          .attr("height", (d) => escalaAltura(d.frecuencia))
          .attr("y", (d) => escalaY(d.frecuencia))
          .selection(),
      (update) =>
        update
          .transition()
          .duration(1000)
          .attr("height", (d) => escalaAltura(d.frecuencia))
          .attr("y", (d) => escalaY(d.frecuencia))
          .attr("x", (d) => escalaX(d.categoria))
          .attr("width", escalaX.bandwidth())
          .selection(),
      (exit) =>
        exit
          .transition()
          .duration(500)
          .attr("y", height - margin.top - margin.bottom)
          .attr("height", 0)
          .remove()
    )
    .on("mouseenter", (_, d) => {
      parrafo.text(`Categor铆a: ${d.categoria}, Frecuencia: ${d.frecuencia}`);
    })
    .on("mouseleave", () => {
      parrafo.text("");
    })
    .on("click", (_, d) => {
      datos.splice(datos.indexOf(d), 1);
      joinDeDatos(datos);
    });
}

const datoNuevoRandom = (datos) => ({
  categoria: String.fromCharCode(
    datos[datos.length - 1].categoria.charCodeAt(0) + 1
  ),
  frecuencia: Math.floor(Math.random() * 800),
});

let datos;

d3.json("datos.json")
  .then((datosCargados) => {
    console.log(datosCargados);
    datos = datosCargados;
    joinDeDatos(datos);
    boton.on("click", () => {
      datos.push(datoNuevoRandom(datos));
      joinDeDatos(datos);
    });
  })
  .catch((error) => console.log(error));
          

Transiciones


Detalles de transiciones en subm贸dulo d3-transition.


  • Tienen un m茅todo delay para fijar un atraso.
  • Tienen un m茅todo ease para alterar como es el cambio en el tiempo.

Join de datos perzonalizado

selecci贸n grupo datos arreglo 4 5 18 23 42
selecci贸n grupo datos arreglo 4 5 18 23 42

              selection.data(data)
            
selecci贸n grupo datos arreglo 4 0 5 1 18 2 23 3 42 4
selecci贸n grupo datos arreglo 4 5 18 23 42

              selection.data(data)
            
selecci贸n grupo datos arreglo 4 0 5 1 18 2 23 3 42 4

              selection.data(data).enter().append("rect")
            
selecci贸n grupo rect rect rect rect rect datos arreglo 4 5 18 23 42
selecci贸n grupo rect rect rect rect rect datos arreglo 4 5 18 23 42

              selection.data(data);
            
selecci贸n grupo rect 0 rect 1 rect 2 rect 3 rect 4 datos arreglo 4 0 5 1 18 2 23 3 42 4
selecci贸n grupo rect rect rect rect rect datos arreglo 4 18 23 42

              selection.data(data);
            
selecci贸n grupo rect 0 rect 1 rect 2 rect 3 rect 4 datos arreglo 4 0 18 1 23 2 42 3

Funci贸n de llave en data join



  const width = 600;
  const height = 400;
  const margin = {
    top: 30,
    bottom: 30,
    right: 30,
    left: 30,
  };
  
  const svg = d3
    .select("body")
    .append("svg")
    .attr("width", width)
    .attr("height", height);
  
  const boton = d3.select("body").append("button").text("Agregar elemento");
  
  const parrafo = d3.select("body").append("p");
  
  const contenedorEjeY = svg
    .append("g")
    .attr("transform", `translate(${margin.left}, ${margin.top})`);
  
  const contenedorEjeX = svg
    .append("g")
    .attr("transform", `translate(${margin.left}, ${height - margin.bottom})`);
  
  const contenedorBarras = svg
    .append("g")
    .attr("transform", `translate(${margin.left} ${margin.top})`);
  
  function joinDeDatos(datos) {
    const maximaFrecuencia = d3.max(datos, (d) => d.frecuencia);
  
    const escalaAltura = d3
      .scaleLinear()
      .domain([0, maximaFrecuencia])
      .range([0, height - margin.top - margin.bottom]);
  
    const escalaY = d3
      .scaleLinear()
      .domain([0, maximaFrecuencia])
      .range([height - margin.top - margin.bottom, 0]);
  
    const ejeY = d3.axisLeft(escalaY);
  
    contenedorEjeY
      .transition()
      .duration(1000)
      .call(ejeY)
      .selection()
      .selectAll("line")
      .attr("x1", width - margin.right - margin.left)
      .attr("stroke-dasharray", "5")
      .attr("opacity", 0.5);
  
    const escalaX = d3
      .scaleBand()
      .domain(datos.map((d) => d.categoria))
      .rangeRound([0, width - margin.right - margin.left])
      .padding(0.5);
  
    const ejeX = d3.axisBottom(escalaX);
  
    contenedorEjeX
      .transition()
      .duration(1000)
      .call(ejeX)
      .selection()
      .selectAll("text")
      .attr("font-size", 20);
  
    contenedorBarras
      .selectAll("rect")
      .data(datos, (d) => d.categoria)
      .join(
        (enter) =>
          enter
            .append("rect")
            .attr("fill", "magenta")
            .attr("y", height - margin.top - margin.bottom)
            .attr("x", (d) => escalaX(d.categoria))
            .attr("width", escalaX.bandwidth())
            .attr("height", 0)
            .transition()
            .duration(1000)
            .attr("height", (d) => escalaAltura(d.frecuencia))
            .attr("y", (d) => escalaY(d.frecuencia))
            .selection(),
        (update) =>
          update
            .transition()
            .duration(1000)
            .attr("height", (d) => escalaAltura(d.frecuencia))
            .attr("y", (d) => escalaY(d.frecuencia))
            .attr("x", (d) => escalaX(d.categoria))
            .attr("width", escalaX.bandwidth())
            .selection(),
        (exit) =>
          exit
            .transition()
            .duration(500)
            .attr("y", height - margin.top - margin.bottom)
            .attr("height", 0)
            .remove()
      )
      .on("mouseenter", (_, d) => {
        parrafo.text(`Categor铆a: ${d.categoria}, Frecuencia: ${d.frecuencia}`);
      })
      .on("mouseleave", () => {
        parrafo.text("");
      })
      .on("click", (_, d) => {
        datos.splice(datos.indexOf(d), 1);
        joinDeDatos(datos);
      });
  }
  
  const datoNuevoRandom = (datos) => ({
    categoria: String.fromCharCode(
      datos[datos.length - 1].categoria.charCodeAt(0) + 1
    ),
    frecuencia: Math.floor(Math.random() * 800),
  });
  
  let datos;
  
  d3.json("datos.json")
    .then((datosCargados) => {
      console.log(datosCargados);
      datos = datosCargados;
      joinDeDatos(datos);
      boton.on("click", () => {
        datos.push(datoNuevoRandom(datos));
        joinDeDatos(datos);
      });
    })
    .catch((error) => console.log(error));
            
selecci贸n grupo rect rect rect rect rect datos arreglo 4 18 23 42

              selection.data(data, (d) => d.llave);
            
selecci贸n grupo rect A rect B rect C rect D rect E datos arreglo 4 A 18 C 23 D 42 E

隆Visualizaci贸n del d铆a!

隆Visualizaci贸n del d铆a!

Art铆culo de explicaci贸n de pandemia COVID-19 mediante simulaciones.

Propuesta por ayudante Francisca Ibarra.

(Fuente: What happens next?)

驴M谩s dudas?

Pr贸ximos eventos:


隆Sesi贸n de jueves 17 de septiembre es libre! Me conectar茅 de todas formas por si tienen preguntas :)

Pr贸ximos eventos:


隆Sesi贸n de jueves 17 de septiembre es libre! Me conectar茅 de todas formas por si tienen preguntas :)


Se publicaron los contenidos de la semana siguiente a la semana de receso. Los ejercicios propuestos se actualizar谩n a medida que est茅n disponibles.

Pr贸ximos eventos:


隆Sesi贸n de jueves 17 de septiembre es libre! Me conectar茅 de todas formas por si tienen preguntas :)


Se publicaron los contenidos de la semana siguiente a la semana de receso. Los ejercicios propuestos se actualizar谩n a medida que est茅n disponibles.


隆Disfruten la semana de receso!

Utilidades en D3.js II

Visualizaci贸n de Informaci贸n

IIC2026 2020-2


隆Deja tus preguntas en los comentarios!